import config from '@config';
import fs from 'node:fs';
import path from 'node:path';
import { getEntry } from 'astro:content';
import matter from 'gray-matter';

type Badge = {
  text: string;
  color: string;
};

type SidebarItem = {
  label: string;
  slug?: string;
  items?: SidebarItem[];
  badge?: Badge;
  collapsed?: boolean;
  link?: string;
  attrs?: Record<string, string>;
};

type SideBarConfigurationItem = {
  label: string;
  items?: SideBarConfigurationItem[];
  slug?: string;
  autogenerated?: {
    directory: string;
    collapsed?: boolean;
  };
  badge?: Badge;
  collapsed?: boolean;
  link?: string;
  attrs?: Record<string, string>;
};

type AdjacentPage = {
  label: string;
  slug: string;
};

type AdjacentPages = {
  prev: AdjacentPage | null;
  next: AdjacentPage | null;
};

const DOCS_DIR = 'docs';

/**
 * Processes auto-generated directory and returns navigation items
 */
const processAutoGeneratedDirectory = async (
  directory: string,
  label: string,
  badge?: Badge,
  collapsed?: boolean
): Promise<SidebarItem> => {
  // @ts-ignore
  const items = fs.readdirSync(path.join(process.env.PROJECT_DIR || '', DOCS_DIR, directory));

  const allItems: SidebarItem[] = [];

  for (const item of items) {
    const fullPath = path.join(process.env.PROJECT_DIR || '', DOCS_DIR, directory, item);
    const isDirectory = fs.statSync(fullPath).isDirectory();

    if (isDirectory) {
      // Recursively process subdirectory
      const subdirResult = await processAutoGeneratedDirectory(
        path.join(directory, item),
        item, // Use directory name as label
        undefined, // No badge for subdirectories
        collapsed // Inherit collapsed state
      );
      allItems.push(subdirResult);
    } else {
      // Process file
      const content = fs.readFileSync(fullPath, 'utf8');
      const { data } = matter(content);

      const astroId = data.slug || path.join(DOCS_DIR, directory, item).replace('.mdx', '');
      const entry = await getEntry('customPages', astroId.toLowerCase());

      if (entry) {
        allItems.push({
          label: entry.data.title,
          slug: entry.data.slug || entry.id.replace(DOCS_DIR, ''),
        });
      }
    }
  }

  return {
    label,
    badge,
    collapsed,
    items: allItems,
  };
};

/**
 * Recursively process sidebar items to handle auto-generated content at any nesting level
 */
const processSidebarItems = async (items: SideBarConfigurationItem[]): Promise<SidebarItem[]> => {
  const processedItems: SidebarItem[] = [];

  for (const item of items) {
    // If item has autogenerated property, process it
    if (item.autogenerated) {
      const processedItem = await processAutoGeneratedDirectory(
        item.autogenerated.directory,
        item.label,
        item.badge,
        item.autogenerated.collapsed !== undefined ? item.autogenerated.collapsed : item.collapsed
      );
      processedItems.push(processedItem);
    }
    // If item has nested items, process them recursively
    else if (item.items && item.items.length > 0) {
      const processedNestedItems = await processSidebarItems(item.items);
      processedItems.push({
        label: item.label,
        slug: item.slug,
        items: processedNestedItems,
        badge: item.badge,
        collapsed: item.collapsed,
      });
    }
    // Otherwise, it's a regular item
    else {
      // if its a link, add it to the processedItems
      if (item.link) {
        processedItems.push({
          label: item.label,
          slug: item.link,
          attrs: item.attrs,
        });
      } else {
        processedItems.push(item as SidebarItem);
      }
    }
  }

  return processedItems;
};

/**
 * Flatten all navigation items into a single array of pages with slugs
 * This is used to find previous and next pages for navigation
 */
const flattenNavigationItems = (items: SidebarItem[]): AdjacentPage[] => {
  const flatPages: AdjacentPage[] = [];

  const processItem = (item: SidebarItem) => {
    // Add the current item if it has a slug
    if (item.slug) {
      flatPages.push({
        label: item.label,
        slug: item.slug,
      });
    }

    // Process nested items if they exist
    if (item.items && item.items.length > 0) {
      item.items.forEach(processItem);
    }
  };

  items.forEach(processItem);
  return flatPages;
};

/**
 * Get the previous and next pages for a given slug
 * Returns null for prev if it's the first page, and null for next if it's the last page
 */
export const getAdjacentPages = async (slug: string): Promise<AdjacentPages> => {
  const navigationItems = await getNavigationItems();
  const flatPages = flattenNavigationItems(navigationItems);

  // Normalize the slug by removing 'docs/' prefix if it exists
  // and ensure consistent formatting with or without leading slash
  let normalizedSlug = slug;
  if (normalizedSlug.startsWith('docs/')) {
    normalizedSlug = normalizedSlug.substring(5); // Remove 'docs/' prefix
  }

  // Find the current page by comparing normalized slugs
  const currentIndex = flatPages.findIndex((page) => {
    // Normalize page slug for comparison
    let pageSlug = page.slug;
    if (pageSlug.startsWith('/')) {
      pageSlug = pageSlug.substring(1);
    }

    return pageSlug === normalizedSlug;
  });

  // If page not found, return null for both prev and next
  if (currentIndex === -1) {
    return { prev: null, next: null };
  }

  // Get previous page if it exists
  const prev = currentIndex > 0 ? flatPages[currentIndex - 1] : null;

  // Get next page if it exists
  const next = currentIndex < flatPages.length - 1 ? flatPages[currentIndex + 1] : null;

  return { prev, next };
};

export const getNavigationItems = async (): Promise<SidebarItem[]> => {
  const configuredSidebar = config.customDocs.sidebar;
  return processSidebarItems(configuredSidebar as SideBarConfigurationItem[]);
};
